//===============================================================================
// Microsoft Architecture Strategy Team
// LitwareHR - SaaS Sample Application
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Configuration;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.DirectoryServices;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;

using Microsoft.Practices.EnterpriseLibrary.Data;

using LitwareHR.Portfolio.Contract;
using LitwareHR.Portfolio.Services.Properties;

using Shp.Runtime.Contract;
using Shp.Runtime.Services;

namespace LitwareHR.Portfolio.Services
{
    static public class ProvisioningLogic
    {
        static private readonly string[] rolesToCreate = new string[]
                                        {
                                            "Administrator",
                                            "EvaluateCandidate",
                                            "ApplyForAJob",
                                            "ManageUsersAndRoles",
                                            "ConfigureAndCustomize",
                                            "OpenPosition"
                                        };

        static public void DoProvisioning(string tenantName, string tenantAlias, int domainModel, string adminPassword)
        {
            try
            {
                tenantAlias = tenantAlias.ToLower();
                if (!TenantExists(tenantAlias))
                {
                    Guid tenantId = Guid.NewGuid();
                    PerformWebServerProvisioning(tenantAlias);
                    ProvisionModule(tenantId, tenantName, tenantAlias, "Runtime", Constants.DB.TenantMetadataStore);
                    ProvisionModule(tenantId, tenantName, tenantAlias, "Recruiting", Constants.DB.TenantMetadataStore);
                    ProvisionModule(tenantId, tenantName, tenantAlias, "Recruiting", Constants.DB.TenantDataStore);
                    PerformAuthenticationProvisioning(tenantAlias, adminPassword);
                }

                else
                {
                    throw new ProvisioningException(String.Format(Resources.TenantExistsException, tenantAlias), null);
                }
            }

            catch (Exception ex)
            {
                throw new ProvisioningException(ex.Message, ex);
            }
        }        

        static private void PerformAuthenticationProvisioning(string tenantAlias, string adminPassword)
        {
            AuthenticationLogic.SetUpTenant(tenantAlias);
            
            Upn newUser = new Upn("Admin", tenantAlias);
            AuthenticationLogic.CreateUser(newUser, adminPassword, string.Format(CultureInfo.InvariantCulture, "Admin@{0}", tenantAlias));

            foreach (string role in rolesToCreate)
            {
                AuthenticationLogic.CreateRole(tenantAlias, role);
                AuthenticationLogic.AddUserToRole(newUser, role);
            }
        }

        static private void PerformWebServerProvisioning(string tenantAlias)
        {
            string serviceHostDeploymentType = ConfigurationManager.AppSettings["ServiceHostDeploymentType"];
            if (serviceHostDeploymentType.Equals("local"))
            {
                bool singleWebsite = Boolean.Parse(ConfigurationManager.AppSettings["SingleWebsite"]);
                string webServer = ConfigurationManager.AppSettings["WebServer"];
                string websiteIdentifier = ConfigurationManager.AppSettings["WebServerWebsiteIdentifier"];
                string publicAppPhysicalPath = ConfigurationManager.AppSettings["PublicAppPhysicalPath"];
                string privateAppPhysicalPath = ConfigurationManager.AppSettings["PrivateAppPhysicalPath"];
                string appServerTenantPhysicalPath = ConfigurationManager.AppSettings["AppServerTenantPhysicalPath"];
                WindowsIdentityImpersonate newIdentity = new WindowsIdentityImpersonate();
                if (newIdentity.Impersonate(ConfigurationManager.AppSettings["ProvisioningUser"], Environment.MachineName, ConfigurationManager.AppSettings["ProvisioningPassword"]))
                {
                    try
                    {
                        // Single Website environments (IIS 5.1) will add tenant virtual folder inside of the tenant provisioning virtual Directory.
                        // That is why that virtual directory name is needed. In Multiple websites environments, the tenant provisioning website identifier is enough.
                        if (singleWebsite)
                        {
                            string tenantProvisioningVirtualDirectory = ConfigurationManager.AppSettings["TenantProvisioningVirtualDirectory"];
                            VirtualDirectoryManager.CreateVirtualFolder(webServer, websiteIdentifier, appServerTenantPhysicalPath, tenantAlias, VirtualDirectoryManager.VirtualFolderType.Web, tenantProvisioningVirtualDirectory);
                            VirtualDirectoryManager.CreateVirtualDirectory(webServer, websiteIdentifier, publicAppPhysicalPath, "public", VirtualDirectoryManager.VirtualDirectoryType.Web, tenantProvisioningVirtualDirectory + "/" + tenantAlias);
                            VirtualDirectoryManager.CreateVirtualDirectory(webServer, websiteIdentifier, privateAppPhysicalPath, "private", VirtualDirectoryManager.VirtualDirectoryType.Web, tenantProvisioningVirtualDirectory + "/" + tenantAlias);
                        }
                        else
                        {
                            VirtualDirectoryManager.CreateVirtualFolder(webServer, websiteIdentifier, appServerTenantPhysicalPath, tenantAlias, VirtualDirectoryManager.VirtualFolderType.Web);
                            VirtualDirectoryManager.CreateVirtualDirectory(webServer, websiteIdentifier, publicAppPhysicalPath, "public", VirtualDirectoryManager.VirtualDirectoryType.Web, tenantAlias);
                            VirtualDirectoryManager.CreateVirtualDirectory(webServer, websiteIdentifier, privateAppPhysicalPath, "private", VirtualDirectoryManager.VirtualDirectoryType.Web, tenantAlias);
                        }
                    }
                    finally
                    {
                        newIdentity.Undo();
                    }
                }
            }
            else if (serviceHostDeploymentType.Equals("distributed"))
            {
                throw new NotImplementedException();
            }
            else
            {
                throw new ConfigurationErrorsException("The appSettings key named ServiceHostDeployment has an invalid value");
            }
        }

        static private bool TenantExists(string tenantAlias)
        {
            try
            {
                AuthorizationLogic.GetTenantIdByAlias(tenantAlias);
                return true;
            }
            catch
            {
                return false;
            }
        }

        static private void ProvisionModule(Guid tenantId, string tenantName, string tenantAlias, string module, string databaseString)
        {
            Database db = DatabaseFactory.CreateDatabase(databaseString);
            using (DbCommand command = db.GetStoredProcCommand("dbo.Provision" + module))
            {
                db.AddInParameter(command, "tenantId", DbType.Guid, tenantId);
                db.AddInParameter(command, "tenantName", DbType.String, tenantName);
                db.AddInParameter(command, "tenantAlias", DbType.String, tenantAlias);
                db.ExecuteNonQuery(command);
            }
        }
    }
}
